import datetime
from flask import g
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import declared_attr
from common.mixins import DictMixin
from common.utils.pinyin import get_first_letter_pinyin
from copy import deepcopy

from exts import db


def session_commit():
    try:
        db.session.commit()
    except SQLAlchemyError as e:
        db.session.rollback()
        raise e
        # reason = str(e)
        # return reason


class BaseModel:

    def get(self, _id):
        return self.query.filter_by(id=_id).first()

    def add(self):
        db.session.add(self)
        return session_commit()

    # def update(self):
    #     return session_commit()

    def delete(self):
        db.session.delete(self)
        return session_commit()

    def add_or_update(self):
        db.session.add(self)
        return session_commit()

    # def as_dict(self):
    #     return {c.name: getattr(self, c.name) for c in self.__table__.columns}


class TimestampMixin(object):
    create_datetime = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    update_datetime = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)


class NormalMixin(object):
    notes = db.Column(db.String(5000))
    is_active = db.Column(db.Boolean, default=True)
    is_delete = db.Column(db.Boolean, default=False)


class UserMixin(object):
    @declared_attr
    def creator_id(cls):
        return db.Column(db.Integer, db.ForeignKey('user.id'))

    @declared_attr
    def updater_id(cls):
        return db.Column(db.Integer, db.ForeignKey('user.id'))


class CompanyMixin(object):
    @declared_attr
    def company_id(cls):
        return db.Column(db.Integer, db.ForeignKey('company.id'))


class BasicModelMixin(NormalMixin, TimestampMixin, UserMixin):
    pass


class CreateMixin:
    @classmethod
    def create(cls, commit=False, **kwargs):
        if not kwargs.get('creator_id'):
            kwargs['creator_id'] = g.user_id
        if not kwargs.get('company_id'):
            kwargs['company_id'] = g.company_id
        new_obj = DictMixin.from_dict(cls, kwargs)
        db.session.add(new_obj)
        if commit is True:
            db.session.commit()
        return new_obj


class CreateWithShortNameMixin:
    @classmethod
    def create(cls, commit=False, **kwargs):
        if not kwargs.get('short_name'):
            kwargs['short_name'] = get_first_letter_pinyin(kwargs.get("name"))
        if not kwargs.get('creator_id'):
            kwargs['creator_id'] = g.user_id
        if not kwargs.get('company_id'):
            kwargs['company_id'] = g.company_id
        new_obj = DictMixin.from_dict(cls, kwargs)
        db.session.add(new_obj)
        if commit is True:
            db.session.commit()
        return new_obj


class UpdateMixin:
    def update(self, commit=False, **kwargs):
        if not kwargs.get('updater_id'):
            kwargs['updater_id'] = g.user_id
        new_obj = DictMixin.from_dict(self, kwargs)

        if commit is True:
            db.session.commit()
        return new_obj

    @classmethod
    def update_many(cls, ids, commit=False, **kwargs):
        objs = cls.query.filter(cls.id.in_(ids)).all()
        for obj in objs:
            obj.update(**kwargs)
        if commit is True:
            db.session.commit()
        return ids


class UpdateWithShortNameMixin:
    def update(self, commit=False, **kwargs):
        old_obj = deepcopy(self)
        if not kwargs.get('updater_id'):
            kwargs['updater_id'] = g.user_id
        new_obj = DictMixin.from_dict(self, kwargs)

        # 当名称发生修改时，自动更新short_name
        if new_obj.name and old_obj.name != new_obj.name:
            new_obj.short_name = get_first_letter_pinyin(new_obj.name)
        if commit is True:
            db.session.commit()
        return new_obj

    @classmethod
    def update_many(cls, ids, commit=False, **kwargs):
        objs = cls.query.filter(cls.id.in_(ids)).all()
        for obj in objs:
            obj.update(**kwargs)
        if commit is True:
            db.session.commit()
        return ids


class DeleteMixin:
    def delete(self, commit=False, updater_id=None):
        self.is_delete = True
        if not updater_id:
            self.updater_id = g.user_id
        else:
            self.updater_id = updater_id
        if commit is True:
            db.session.commit()
        return self

    def hard_delete(self, commit=False):
        db.session.delete(self)
        if commit is True:
            db.session.commit()
        return self.id

    @classmethod
    def hard_delete_many(cls, ids, commit=False):
        objs = cls.query.filter(cls.id.in_(ids)).all()
        [db.session.delete(u) for u in objs]
        if commit is True:
            db.session.commit()
        return ids


